Tool
A tool the LLM can call. There are two idiomatic ways to define one:
1. Extend it — for a reusable, dependency-injected tool that deserves a name (mirrors how a concrete agent extends ToolLoopAgent):
class SearchDocsTool(private val repo: DocRepository) :
Tool<SearchInput, List<SearchResult>, AppContext>(
name = "searchDocs",
description = "Search the product documentation",
inputSerializer = serializer(),
outputSerializer = serializer(),
) {
override suspend fun ToolExecutionContext<AppContext>.execute(input: SearchInput) =
repo.search(input.query)
}
// usage: toolSetOf(SearchDocsTool(repo)) — one class, many instancesOverride only the hooks you need (needsApproval, toModelOutput, onInputStart, onInputDelta, onInputAvailable); they no-op by default. For a tool that emits preliminary snapshots before its final value, extend StreamingTool and override StreamingTool.executeStream instead.
2. Use the tool / streamingTool factories — for trivial inline tools where a named class is overkill. The factories build an anonymous subclass.
Per invariant I-2, tools use inputSchema (v6) — never parameters (v5, removed). Per I-8 tools are stateless: inputs in, outputs out, no shared mutable state — so a single instance is safely reused across agents.
Type Parameters
Application-defined input type, must be @Serializable. The model produces JSON matching its schema; the agent deserializes via inputSerializer before calling execute.
Application-defined output type, must be @Serializable. Serialized into the model's tool-result message via outputSerializer, or summarized via toModelOutput if the raw output would be too verbose for the model.
Caller-supplied typed context propagated through the whole loop (set via callOptionsSchema, accessed inside execute as this.context).
Internally the agent loop always drives a Flow (the v6 alignment per tool.ts:68-71's AsyncIterable<OUTPUT> shape): a plain Tool wraps its single execute value in a one-emission flow; a StreamingTool passes its Flow straight through. The loop collects with one-step lookahead — non-final emissions become StreamEvent.ToolResult(preliminary = true), the last emission is the final result that lands in the model's message log.
Inheritors
Constructors
Properties
Few-shot example inputs encoded as JSON strings. Mirrors v6's tool.inputExamples (per historical parity gap #19). Disproportionate quality lift on small models — Gemma 4 E2B's tool-call accuracy notably improves with 2-3 in-tool examples. ToolSet.descriptors inlines these into the description as Example: <json> so every provider sees them (most don't expose a separate examples field).
Application-defined metadata bag. Mirrors v6's tool.metadata (per historical parity gap #34). Opaque to the loop — consumers (logger middleware, telemetry, host-side gating) can read it via ToolSet.byName. Examples: feature-flag key, billing tier, owning team. Empty by default.
Provider-specific config for this tool, sent to the model on the wire (e.g. Anthropic cache_control). Distinct from metadata, which is host-side only. Mirrors v6's tool.providerOptions. Empty by default.
Functions
Run the tool — the common single-value case. The model's JSON input has already been deserialized into TInput; return the typed output. this is the ToolExecutionContext, so the typed context, abortSignal, toolCallId, stepNumber, messages, and writer are in scope.
Approval gate (parity gap #17). Return true to require host approval before execute runs: the loop ends with the call surfaced in GenerateResult.pendingApprovals until the host resumes. The options carry toolCallId + messages + experimental_context, so the gate can decide from conversation history or call identity. Default: never gates.
Lifecycle hook (gap #18) — fired as the input JSON streams in token-by-token (StreamEvent.ToolInputDelta). The accumulated input isn't yet valid JSON; this is for raw-character pre-warm. No-op by default.
Lifecycle hook (gap #18) — fired when the model commits to calling this tool (StreamEvent.ToolInputStart). Pre-warm here: UI spinner, cache priming, etc. No-op by default.
Model-visible summary (parity gaps #17 + #14). Return a structured ToolResultOutput (Text / Json / Error / …) to send the model a short summary in place of the full (possibly verbose) output; the loop bridges it to ContentPart.ToolResult.modelVisible (+ isError). Return null (default) to send the raw output as-is.